const $ = require('jquery');
const fs = require('fs');
const path = require('path');
const PeerManager = require('../../index').PeerManager;

function createDialogue(peerManager, conn = null) {
  
  const tpl = `
    <div class="dialogue">
      <a class="close-btn">关闭</a>
      <div class="remote-peer-info">
        <input type="text" style="width: 210px" placeholder="请输入远端peer_id">
      </div>

      <div class="recives">
        <span class="text"></span><br>
        <progress class="download-progress" hidden></progress>
        <span class="reviced-time"></span>
      </div>

      <div class="sends sends-text">
        <textarea name="message" cols="30" rows="2" placeholder="请输入发送的消息"></textarea>
        <button>发送</button>
      </div>
      
      <div class="sends sends-file">
        <input type="file" name="files" placeholder="选择传输的文件">
        <button>发送</button>
      </div>

      <div class="error">
        <p class="text"></p>
        <p class="occur-time"></p>
      </div>
    </div>
  `;

  const $dialoguesContainer = $('#dialogues-container')
    , $dialogue = $(tpl)
    , $remotePeerInfo = $dialogue.find('.remote-peer-info')
    , $recives = $dialogue.find('.recives')
    , $sends = $dialogue.find('.sends')
    , $error = $dialogue.find('.error');

  function listenConn(conn) {
    conn.on('open', function () {
      $remotePeerInfo.find('input').attr('disabled', true);
      $remotePeerInfo.find('button').text('断开').removeAttr('disabled');
      $sends.find('textarea,button,input').removeAttr('disabled');
    });
    conn.on('close', function () {
      $remotePeerInfo.find('input').removeAttr('disabled');
      $remotePeerInfo.find('button').text('连接').removeAttr('disabled');
      $sends.find('textarea,button,input').attr('disabled', true);
    });
    conn.on('data', function (buffer) {
      /**
       * send-buffer:
       * | headerStrSize |          headerStr         | 
       * |                  payload                   |
       */
      const headerStrSize = buffer.readInt16BE(0)
        , header = JSON.parse(buffer.toString('utf8', 2, headerStrSize + 2))
        , payload = buffer.slice(2 + headerStrSize);

      const now = new Date();
      // console.log(header, payload);
      if (header.type === 'text') {
        $recives.find('.text').text(payload.toString());
        $recives.find('.download-progress').attr({'hidden': 'true'});
      } else if (header.type === 'file') {
        const $downloadProgress = $recives.find('.download-progress');
        if (header.position === 0) {
          $recives.find('.text').text(`接收 "${header.name}" ...`);
          const downloadProgress = $downloadProgress[0];
          downloadProgress.max = header.total;
          $downloadProgress.removeAttr('hidden');
        }
        $downloadProgress[0].value = header.position;
      }
      $recives.find('.reviced-time').text(now.toLocaleString());
    });
    conn.on('error', function (err) {
      const now = new Date();
      $remotePeerInfo.find('input').removeAttr('disabled');
      $remotePeerInfo.find('button').text('连接').removeAttr('disabled');
      $sends.find('textarea,button,input').attr('disabled', true);

      $error.find('.text').text(err);
      $error.find('.occur-time').text(now.toLocaleString());
    });
  }

  function sendData(header, payload) {
    /**
     * send-buffer:
     * | headerStrSize |          headerStr         | 
     * |                  payload                   |
     */
    const headerStr = JSON.stringify(header),
      headerStrSize = headerStr.length,
      payloadSize = payload.length,
      totalSize = 2 + headerStrSize + payloadSize;

    if (headerStrSize >= 2048) {
      throw new Error('header(JSON.stringify) is too big, >=2048');
    }
    if (totalSize >= 65535) {
      throw new Error('package is too big, >= 65535');
    }

    let buffer = Buffer.allocUnsafe(2 + headerStrSize);
    buffer.writeInt16BE(headerStrSize, 0);
    buffer.write(headerStr, 2, headerStrSize);

    conn.send(Buffer.concat([buffer, payload]));
  }

  function _sendFile(fd, fileInfo, blockSize) {
    const fileSize = fileInfo.total;
    let position = fileInfo.position;
    const length = Math.min(fileSize - position, blockSize);

    fileInfo.length = length;
    let buffer = new Buffer(length);

    fs.read(fd, buffer, 0, length, position, (err, bytesRead, buffer) => {
      if (err) {
        console.error(err);
        return;
      }
      sendData(fileInfo, buffer);
      position += length;
      // console.log(`发送了 ${length} bytes.`);
      if (fileSize > position) {
        fileInfo.position = position;
        _sendFile(fd, fileInfo, blockSize);
      } else {
        console.log('发送完成.');
      }
    })
  }

  function sendFile(conn, filePath) {
    fs.stat(filePath, (err, stats) => {
      const blockSize = 1024;
      const fileInfo = {
        type: 'file',
        name: path.basename(filePath),
        hash: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
        total: stats.size,
        position: 0,
        length: 0,
        payloadType: 'buffer',
      }

      fs.open(filePath, 'r', (err, fd) => {
        if (err) {
          if (err.code === 'ENOENT') {
            alert(`文件"${file.path}"不存在.`);
            return;
          }
          throw err;
        }
        console.log('文件打开成功.');
        _sendFile(fd, fileInfo, blockSize);
      });
    });
  }

  if (!conn) {
    window.myConn = conn;
    $remotePeerInfo.append('<button>连接</button>');
    $sends.find('textarea,button').attr('disabled', true);
  } else {
    const peerId = conn.peerId;
    $remotePeerInfo.find('input').val(peerId).attr('disabled', true);
    $remotePeerInfo.append('<button>断开</button>');
    $sends.find('textarea,button').removeAttr('disabled');
    listenConn(conn);
  }

  // 连接/断开
  $remotePeerInfo.find('button').on('click', function () {
    $btn = $(this);
    if (!conn) {
      const remotePeerId = $remotePeerInfo.find('input').val().trim();
      if (!remotePeerId) {
        alert('Peer id 不能为空!');
        return;
      }
      $btn.attr('disabled', true);
      conn = peerManager.connect(id = remotePeerId, options = {
        label: 'hi',
        metadata: {
          peer_id: peerManager.id
        },
        serialization: 'json',
      });
      window.myConn = conn;
      listenConn(conn);
    } else {
      conn.close();
      conn = null;
      $btn.attr('disabled', true);
      $remotePeerInfo.find('button').text('连接');
      $remotePeerInfo.find('button').removeAttr('disabled');
      $sends.find('textarea,button').attr('disabled', true);
    }
  });

  // 发送消息
  $dialogue.find('.sends-text button').on('click', function () {
    if (!conn) {
      alert('未连接!');
      return;
    }
    const message = $dialogue.find('.sends-text textarea').val();
    sendData({ type: 'text', payloadType: 'string' }, new Buffer.from(message));
  });

  // 发送文件
  $dialogue.find('.sends-file button').on('click', function () {
    if (!conn) {
      alert('未连接!');
      return;
    }
    const files = $dialogue.find('.sends-file input')[0].files;
    if (files.length === 0) {
      alert('请选择一个有效的文件!');
      return;
    }

    const file = files[0];
    console.log(file);
    sendFile(conn, file.path);
  });

  // 关闭对话
  $dialogue.find('>.close-btn').on('click', function() {
    if (conn) {
      conn.close();
    }
    $dialogue.remove();
  })

  $dialoguesContainer.append($dialogue);
}

$(window.document).ready(function () {
  console.log('获取websocket token');

  innerServices.webbtConfigerAPI.get({}, res => {
    const config = res.data;
    const peerManager = new PeerManager(config.peerId, {
      rtcConfig: config.rtcConfig,
      wsConfig: config.wsConfig,
      autoCloseConncetWhenErrorOccur: true, // 自动关闭出错链接
    });

    peerManager.on('ready', function (id) {
      $('#peer_id').text(id);
      window.isInitFinish = true;
    });
    peerManager.on('connection', function (conn) {
      createDialogue(peerManager, conn);
    });
    peerManager.on('error', function (err) {
      $('#error').text(err);
    });

    $('#create-btn').on('click', function (event) {
      createDialogue(peerManager);
    });

    window.peerManager = peerManager;
  }, error => {
    console.error('获取 websocket token 失败!');
  });
});
